`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: HITSZ
// Engineer: 
// 
// Create Date: 2023/11/02 
// Design Name: 
// Module Name: remote_fpga
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module remote_fpga # 
(
    // the number of switches
    parameter integer SWITCH_WIDTH = 24,

    // the number of leds
    parameter integer LED_WIDTH = 24,
    
    // the number of buttons
    parameter integer BUTTON_WIDTH = 5,
    
    // for the purpose of simplifying, we treat the segment displays as a whole
    // the number of segment displays 
    // parameter integer SEG_NUMBER = 1,

    // the number of segment display's segments
    parameter integer SEG_SEGMENT_WIDTH = 8,

    // the number of segment display's bits
    parameter integer SEG_BIT_WIDTH = 8,

    // 1 if the board has a keyboard else 0
    parameter HAS_KEYBOARD = 1

    /// Because it is normal for 4x4 keyboard, we will make it inmutable.
    /// the number of keyboard's rows
    // parameter integer KEYBOARD_ROW_WIDTH = 4,
    /// the number of keyboard's colomns
    // parameter integer KEYBOARD_COL_WIDTH = 4         
)
(
    input wire clk,
    input wire rst,
    input wire rx,
    input wire [3:0]   keyboard_col,
    input wire [LED_WIDTH - 1:0]            led,
    // we flatten the array of segment displays because the input type mustn't be array
    input wire [SEG_SEGMENT_WIDTH - 1:0]    seg_segment,
    input wire [SEG_BIT_WIDTH - 1:0]        seg_bit,
 
    output wire [SWITCH_WIDTH - 1:0]        switch,
    // for buttons, when uart_rx carries a button signal, turn on this button for 100ms
    output wire [BUTTON_WIDTH - 1:0]        button,
	output wire [3:0]  keyboard_row,     
    output wire tx
);
    localparam integer baud_rate = 230400;
    localparam integer cycle_per_byte = 1000000000/baud_rate;
    // sample_data_bytes，采样数据的bytes数，minisys应等于5
    localparam integer sample_data_bytes =  (SEG_SEGMENT_WIDTH + SEG_BIT_WIDTH + LED_WIDTH) % 8 == 0 ?
                                            (SEG_SEGMENT_WIDTH + SEG_BIT_WIDTH + LED_WIDTH) / 8 :
                                            (SEG_SEGMENT_WIDTH + SEG_BIT_WIDTH + LED_WIDTH) / 8 + 1;
    // max_dcnt，发送last_data的最大次数，minisys应等于1480
    localparam integer max_dcnt = 2 * ((32 * baud_rate / 10000) + (sample_data_bytes - (32 * baud_rate / 10000) % sample_data_bytes));
    
    wire [7:0] tx_data;
    wire rstn = ~rst;
    // init_end = 1 -> enable ip core interrupt
    reg init_end = 0;
    //  Interrupt Control - The AXI UART Lite core provides interrupt enable/disable 
    //  control. If interrupts are enabled, a rising-edge sensitive interrupt is generated 
    //  when the receive FIFO becomes non-empty or when the transmit FIFO becomes empty
    (*MARK_DEBUG = "TRUE"*)wire w_uart_int;
    
    // aw, address write channel
    // awaddr  0h: rx_FIFO    04h: tx_FIFO    08h: STAT_REG    0Ch: CTRL_REG
    // rx_FIFO[DataBits-1:0] -> UART receive data
    // tx_FIFO[DataBits-1:0] -> UART transmit data
    // CTRL_REG[0] == 1 -> clear the transmit FIFO else do nothing
    // CTRL_REG[1] == 1 -> clear the receive FIFO else do nothing
    // CTRL_REG[4] == 1 -> enable interrupt signal else disable it
    // STAT_REG[0] == 1 -> Receive FIFO has data else it is empty
    // STAT_REG[1] == 1 -> Receive FIFO is full else is not full
    // STAT_REG[2] == 1 -> Transmit FIFO is empty else is not empty
    // STAT_REG[3] == 1 -> Transmit FIFO is full else is not full
    // STAT_REG[4] == 1 -> Interrupt is enabled else is disabled
    // STAT_REG[5] == 1 -> Overturn error has occured, which means receive FIFO is full,
    // a new character is ignored and not written into the receive FIFO
    // STAT_REG[6] == 1 -> Frame error has occured, which means receiving a new character 
    // with stop bit 0. The new character is ignored.
    // STAT_REG[7] == 7 -> Parity error has occurred.
    (*MARK_DEBUG = "TRUE"*)reg [3:0]s_axi_awaddr = 0;
    (*MARK_DEBUG = "TRUE"*)reg s_axi_awvalid = 0;
    (*MARK_DEBUG = "TRUE"*)wire s_axi_awready;
    // w, write data channel
    (*MARK_DEBUG = "TRUE"*)reg [31:0]s_axi_wdata = 0;
    // in axi_lite, wstrb is ignored
    (*MARK_DEBUG = "TRUE"*)reg [3:0]s_axi_wstrb = 4'hf;
    (*MARK_DEBUG = "TRUE"*)reg s_axi_wvalid = 1'b0;
    (*MARK_DEBUG = "TRUE"*)wire s_axi_wready;
    // b, write back resp channel
    // bresp/rresp  00: OKAY    01:EXOKAY    10:SLVERR    11:DECERR
    // DECERR is never be generated in this ip core
    // SLVERR: read the empty receive FIFO or write the full transimit FIFO
    (*MARK_DEBUG = "TRUE"*)wire [1:0]s_axi_bresp;
    (*MARK_DEBUG = "TRUE"*)wire s_axi_bvalid;
    (*MARK_DEBUG = "TRUE"*)reg s_axi_bready = 1'b0;
    // ar, address read channel
    (*MARK_DEBUG = "TRUE"*)reg [3:0]s_axi_araddr = 4'h0;
    (*MARK_DEBUG = "TRUE"*)reg s_axi_arvalid = 1'b0;
    (*MARK_DEBUG = "TRUE"*)wire s_axi_arready;
    // r, read data channel
    (*MARK_DEBUG = "TRUE"*)wire [31:0]s_axi_rdata;
    (*MARK_DEBUG = "TRUE"*)wire [1:0]s_axi_rresp;
    (*MARK_DEBUG = "TRUE"*)wire s_axi_rvalid;
    (*MARK_DEBUG = "TRUE"*)reg s_axi_rready = 1'b0;
    
    (*MARK_DEBUG = "TRUE"*)reg [7:0] receive_data = 0;
    
    // wint = 1 -> you can write to axi
    reg[31:0] cnt = 0;
    wire wint = (cnt == cycle_per_byte);
    reg reg_wint = 0;
    axi_uartlite_0 uart_lite
    (
        .s_axi_aclk(clk),
        .s_axi_aresetn(rstn),

        .interrupt(w_uart_int),
        .s_axi_awaddr(s_axi_awaddr),
        .s_axi_awvalid(s_axi_awvalid),
        .s_axi_awready(s_axi_awready),
        .s_axi_wdata(s_axi_wdata),
        .s_axi_wstrb(s_axi_wstrb),
        .s_axi_wvalid(s_axi_wvalid),
        .s_axi_wready(s_axi_wready),
        .s_axi_bresp(s_axi_bresp),
        .s_axi_bvalid(s_axi_bvalid),
        .s_axi_bready(s_axi_bready),
        .s_axi_araddr(s_axi_araddr),
        .s_axi_arvalid(s_axi_arvalid),
        .s_axi_arready(s_axi_arready),
        .s_axi_rdata(s_axi_rdata),
        .s_axi_rresp(s_axi_rresp),
        .s_axi_rvalid(s_axi_rvalid),
        .s_axi_rready(s_axi_rready),
            
        .rx(rx),
        .tx(tx)
    );
    
    (*MARK_DEBUG = "TRUE"*)wire sample_en;
    wire is_duplicated;
    reg[10:0] duplication_cnt;

    // write
    always@(posedge clk or negedge rstn) begin
        if (~rstn) begin
            init_end <= 0;
            s_axi_awaddr <= 0;
            s_axi_awvalid <= 0;
            s_axi_wdata <= 0;
            s_axi_wvalid <= 0;
            s_axi_bready <= 0;
            reg_wint <= 0;
            duplication_cnt <= 0;
        end
        // start enabling interrupt
        else if (~init_end & ~s_axi_bvalid) begin
            s_axi_awaddr <= 4'hc;
            s_axi_awvalid <= 1'b1;
            s_axi_wdata <= 32'h13;
            s_axi_wvalid <= 1'b1;
            s_axi_bready <= 1'b1;
        end
        // end enabling interrupt
        else if (~init_end & s_axi_bvalid) begin
            init_end <= 1'b1;
            s_axi_awaddr <= 0;
            s_axi_awvalid <= 0;
            s_axi_wdata <= 0;
            s_axi_wvalid <= 0;
            s_axi_bready <= 0;
        end
        // sampling
        else if (init_end & wint & ~s_axi_bvalid & sample_en) begin
            // if is_duplicated == 0, just write new data
            if (~is_duplicated) begin
                s_axi_awaddr <= 4'h4;
                s_axi_awvalid <= 1'b1;
                s_axi_wdata <= tx_data;
                s_axi_wvalid <= 1'b1;
                s_axi_bready <= 1'b1;
                reg_wint <= 1'b1;
                duplication_cnt <= 0;
            end
            else begin
                // else write old data at most max_dcnt/5 times
                if (duplication_cnt >= max_dcnt) begin
                    reg_wint <= 1'b0;
                    s_axi_awaddr <= 0;
                    s_axi_awvalid <= 0;
                    s_axi_wdata <= 0;
                    s_axi_wvalid <= 0;
                    s_axi_bready <= 0;
                end
                else begin
                    duplication_cnt <= duplication_cnt + 1;
                    s_axi_awaddr <= 4'h4;
                    s_axi_awvalid <= 1'b1;
                    s_axi_wdata <= tx_data;
                    s_axi_wvalid <= 1'b1;
                    s_axi_bready <= 1'b1;
                    reg_wint <= 1'b1;
                end
            end
        end
        else if (init_end & reg_wint & ~s_axi_bvalid & sample_en) begin
            s_axi_awaddr <= 4'h4;
            s_axi_awvalid <= 1'b1;
            s_axi_wdata <= tx_data;
            s_axi_wvalid <= 1'b1;
            s_axi_bready <= 1'b1;
            reg_wint <= 1'b1;
        end
        else begin
            s_axi_awaddr <= 0;
            s_axi_awvalid <= 0;
            s_axi_wdata <= 0;
            s_axi_wvalid <= 0;
            s_axi_bready <= 0;
            reg_wint <= 1'b0;
        end
    end
    
    // read
    (*MARK_DEBUG = "TRUE"*)reg dc_en = 0;
    always@(posedge clk or negedge rstn) begin
        if (~rstn) begin
            s_axi_araddr <= 0;
            s_axi_arvalid <= 0;
            s_axi_rready <= 0;
            receive_data <= 0;
            dc_en <= 0;
        end
        // noting that w_uart_int will be assigned to 1 when tx_fifo is empty as well
        // so receive_data will be XX in this case
        else if (init_end & w_uart_int) begin
            s_axi_araddr <= 4'h0;
            s_axi_arvalid <= 1'b1;
            s_axi_rready <= 1'b1;
            dc_en <= 1'b0;
        end
        else if (init_end) begin
            if (s_axi_rvalid) begin
                s_axi_araddr <= 4'h0;
                s_axi_arvalid <= 1'b0;
                s_axi_rready <= 1'b0;
                if (s_axi_rresp != 0) begin
                    receive_data <= receive_data;
                    dc_en <= 1'b0;
                end
                else begin
                    receive_data <= s_axi_rdata[7:0];
                    dc_en <= 1'b1;
                end
            end
            else begin
                s_axi_araddr <= s_axi_araddr;
                s_axi_arvalid <= s_axi_arvalid;
                s_axi_rready <= s_axi_rready;
                dc_en <= 1'b0;
            end
        end
        else begin
            s_axi_araddr <= s_axi_araddr;
            s_axi_arvalid <= s_axi_arvalid;
            s_axi_rready <= s_axi_rready;
            dc_en <= 1'b0;
        end
    end
    
    always@(posedge clk or negedge rstn) begin
        if (~rstn || ~sample_en) begin
            cnt <= 0;
        end
        else if (init_end && cnt <= cycle_per_byte) begin
            cnt <= cnt + 1;
        end
        else begin
            cnt <= 0;
        end
    end
    
    (*MARK_DEBUG = "TRUE"*)wire group_done;
    
    rx_decode # (
        .SWITCH_WIDTH(SWITCH_WIDTH),
        .BUTTON_WIDTH(BUTTON_WIDTH),
        .HAS_KEYBOARD(HAS_KEYBOARD)
    ) rx_decode_0(
        .clk(clk),
        .rstn(rstn),
        .code(receive_data),
        .decode_en(dc_en),
        .keyboard_col(keyboard_col),
        .group_done(group_done & ~reg_wint),
        .switch(switch),
        .button(button),
        .keyboard_row(keyboard_row),
        .sample_en(sample_en)
    );
    
    sampling # (
        .LED_WIDTH(LED_WIDTH),
        .SEG_SEGMENT_WIDTH(SEG_SEGMENT_WIDTH),
        .SEG_BIT_WIDTH(SEG_BIT_WIDTH)
    )sampling_0(
        .clk(clk),
        .rstn(rstn),
        .led(led),
        .seg_segment(seg_segment),
        .seg_bit(seg_bit),
        .tx_done(wint),
        .sample_en(sample_en),
        .group_done(group_done),
        .tx_data(tx_data),
        .is_duplicated(is_duplicated)
    );
endmodule

